import os
import math
import time
import numpy as np
import tkinter as tk
import tkinter.messagebox as tkm
from pathlib import Path

import cv2
import torch
import logging
import datetime
import configparser
import torch.backends.cudnn as cudnn
from tkinterdnd2 import *
import tkinter.filedialog as tkf
from models.experimental import attempt_load
from utils.datasets import LoadStreams, LoadImages
from utils.general import non_max_suppression, scale_coords, xyxy2xywh, set_logging, increment_path, save_one_box, \
    MyThread, imgConvert, ToolTip, LoggerBox
from utils.plots import colors, plot_one_box
from utils.torch_utils import time_synchronized

content = configparser.ConfigParser()
content.read("config.ini", encoding='UTF-8')

# 输入图像为640x480
file = content.get("detect", "file")
camera = "0"
weight = content.get("detect", "weight")
conf_thres = eval(content.get("detect", "conf_thres"))
iou_thres = eval(content.get("detect", "iou_thres"))
bg = content.get("detect", "bg_image")
# 计算设备【cpu|gpu】
computer_device = content.get("detect", "computer_device")
# 检测类型【file|camera】
detect_type = content.get("detect", "detect_type")

# 画布
canvas = None
# 临时变量引用canvas，防止不生效
file_temp = None
# 输出文件夹
save_dir = None
# 模型
model = None
# 类别数组
names = None
# Set Dataloader
vid_path, vid_writer, dataset = None, None, None
# 主窗口
root = None
# 停止检测
stop_detect = False
# 日志
log_box, log_path = None, content.get("detect", "log_path")
# 选择文件根据文件和摄像头不同的检测方式禁用
select_file_entry, select_file_Btn = None, None
# 重置系统，检测过程不可用
reset_system_btn = None

# 可检测文件类型
image_formats = ['bmp', 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'dng', 'webp', 'mpo']  # 可检测图片格式
video_formats = ['mov', 'avi', 'mp4', 'mpg', 'mpeg', 'm4v', 'wmv', 'mkv']  # 可检测视频格式



################################################################# 检测实现 #############################################################
@torch.no_grad()
def start_detect():

    global dataset, vid_path, model, vid_writer, canvas, root, stop_detect, \
        computer_device, detect_type, file, camera, weight, file_temp, save_dir, reset_system_btn


    if model == None:
        print_save_log("检测终止")
        print_save_log("模型尚未初始化，请先加载模型")
        return

    # 开始检测，禁用重置系统
    reset_system_btn.config(state='disable')

    print_save_log(f"计算设备：{computer_device}")
    print_save_log(f"检测类型：{detect_type}")
    print_save_log(f"加载权重：{weight}")
    print_save_log(f"网络结构\n{model}")
    print_save_log(f"输出路径：{save_dir}")

    # 自动时创建增量路径及txt文件夹
    (save_dir / 'labels').mkdir(parents=True, exist_ok=True)  # make dir

    # 初始化改变停止标志防止无法检测
    stop_detect = False

    # 摄像头和图片检测分开处理加载数据源
    if detect_type == "camera":
        print_save_log("即将进行摄像头检测")
        print_save_log(f"检测对象：{camera}号摄像头")
        # 读取视频流
        cudnn.benchmark = True  # set True to speed up constant image size inference
        dataset = LoadStreams(camera)

    if detect_type == "file":
        print_save_log("即将进行文件检测")
        print_save_log(f"检测对象：{file}")
        # 读取图片/视频
        dataset = LoadImages(file)

    # 重新指向计算设备为cpu或0（gpu）
    device = computer_device
    if computer_device == "gpu":
        device = "0"

    t0 = time.time()
    # 从上面的LoadImages()就可以看到每次只输出单张图片,这边img: (C,H,W)
    # 如果每次要输出好几张图片传入模型,那么就需要修改LoadImages()了,那么这里img才会变成(N,C,H,W)
    for path, img, im0s, vid_cap in dataset:
        # 处理的对象是视频
        if hasattr(dataset, 'frame'):
            print_save_log(f'正在处理视频 {file} (第{dataset.frame}帧/共{dataset.frames}帧)')
        # 代表检测是图片
        elif detect_type != "camera":
            print_save_log(f'正在处理图片 {file}')
        # 读取图片,归一化等操作
        img = torch.from_numpy(img).to(device).float() / 255.0
        # 如果是单张图片，那么(C,H,W)升维成(1,C,H,W)
        if img.ndimension() == 3:
            img = img.unsqueeze(0)
        # Inference
        t1 = time_synchronized()
        ''' 
            获得预测的结果pred的结果为例如为：torch.Size([1, 18900, 85])， img为(1,3,640,480),
            模型的下采样倍率为[16,32],(640/32*480/32+640/16*480/16)*3=4500,也就是特征图上格子数.
        '''
        pred = model(img, augment=False)[0]

        # 进行非极大值抑制
        pred = non_max_suppression(pred, conf_thres=conf_thres, iou_thres=iou_thres)
        t2 = time_synchronized()
        # 计算fps
        fps = (int((1 / (t2 - t1))*100))/100
        # 检测过程
        for i, det in enumerate(pred):  # detections per image
            if detect_type == "camera":  # batch_size >= 1
                p, s, im0, frame = path[i], f'{i}: ', im0s[i].copy(), dataset.count
            else:
                p, s, im0, frame = path, '', im0s.copy(), getattr(dataset, 'frame', 0)
            # 定义图片,txt等存储的地址
            p = Path(p)  # to Path
            save_path = str(save_dir / p.name)  # img.jpg
            txt_path = str(save_dir / 'labels' / p.stem) + (
                '' if dataset.mode == 'image' else f'_{frame}')  # img.txt
            s += '图像大小「%g, %g」' % img.shape[2:]  # print string
            # 因为pred结果的box是在加边(padding)后的图片上的坐标,所以要还原到原图的坐标
            gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh
            imc = im0.copy()  # for opt.save_crop
            # 无目标时显示fps
            plot_one_box(None, im0, label=None, color=(255, 0, 255), line_thickness=2, fps=fps)
            # 包含目标
            if len(det):
                # 加边后图的坐标转为原图坐标
                # Rescale boxes from img_size to im0 size
                det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()
                # Print results
                # 统计目标数
                object_num = 0
                s += "类别「"
                for c in det[:, -1].unique():
                    n = (det[:, -1] == c).sum()  # detections per class
                    # 统计类别
                    s += f"{n}个{names[int(c)]} "  # add to string
                    object_num += 1
                # 去掉尾部|和空格
                s = s[0:-2]
                s += f"」共{object_num}个目标"
                # Write results
                for *xyxy, conf, cls in reversed(det):
                    # 保存检测到的标签txt
                    xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()  # normalized xywh
                    # line = (cls, *xywh, conf) if opt.save_conf else (cls, *xywh)  # label format
                    line = (cls, *xywh, conf)  # label format
                    with open(txt_path + '.txt', 'a') as f:
                        f.write(('%g ' * len(line)).rstrip() % line + '\n')
                    # 给推理的图片加box
                    # 保存检测图
                    c = int(cls)  # integer class
                    # 图片上绘制的文字
                    label = f'{names[c]} {conf:.2f}'
                    # 将框画在图中
                    plot_one_box(xyxy, im0, label=label, color=colors(c, True), line_thickness=3,
                                 fps=fps)
                    # 将检测到的物体裁剪保存
                    save_one_box(xyxy, imc, file=save_dir / 'crops' / names[c] / f'{p.stem}.jpg', BGR=True)

            # 未检测到目标
            else:
                s += "未检测到目标"

            # 输出目标分类信息、目标总数 单帧用时、fps信息
            print_save_log(f'{s} 用时{t2 - t1:.3f}s {fps}fps')
            # 绘图至canvas
            tkimg = imgConvert.convert(im0)
            canvas.create_image(0, 0, anchor='nw', image=tkimg, tag='im0')
            file_temp = tkimg
            root.update()
            root.after(1)

            # 文件类型且格式为图片的单张图片检测显示
            if detect_type == "file" and file.split(".")[-1] in image_formats:
                tkimg = imgConvert.convert(im0, resize=True)
                file_temp = tkimg
                canvas.create_image(0, 0, anchor='nw', image=tkimg, tag='im0')
                root.update()
                # 检测退出，还原重置系统
                reset_system_btn.config(state='normal')
                return

            # 停止检测
            if stop_detect:
                # 以tag为标记删除背景
                reset_canvas()
                print_save_log("停止检测")
                # 停止检测标志更新
                stop_detect = False

                # 释放摄像头
                dataset.cap.release()
                cv2.destroyAllWindows()

                root.update()
                # 检测退出，还原重置系统
                reset_system_btn.config(state='normal')
                return

            # 保存结果，图片就保存图片，视频就保存视频片段
            if dataset.mode == 'image':
                cv2.imwrite(save_path, im0)
                image = im0
            if dataset.mode == 'video' or dataset.mode == 'stream':  # 'video' or 'stream'
                if vid_path != save_path:  # new video
                    vid_path = save_path
                    if isinstance(vid_writer, cv2.VideoWriter):
                        vid_writer.release()  # release previous video writer
                    if vid_cap:  # video
                        fps = vid_cap.get(cv2.CAP_PROP_FPS)
                        w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                        h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                    else:  # stream
                        fps, w, h = 30, im0.shape[1], im0.shape[0]
                        save_path += '.mp4'
                    vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
                vid_writer.write(im0)
    # 保存图和txt
    s = f"\n{len(list(save_dir.glob('labels/*.txt')))} 标签文件保存到 {save_dir / 'labels'}"
    print_save_log(f"检测结果保存到 {save_dir}{s}")
    print_save_log(f'检测完成，用时{time.time() - t0:.3f}s')
    # 释放摄像头
    dataset.cap.release()
    cv2.destroyAllWindows()
############################################################### 检测实现 END ############################################################



############################################################### 初始化窗口参数 ###########################################################
def init_window():
    global root
    root = TkinterDnD.Tk()
    root.title('口罩佩戴检测')  # 窗口标题
    root.geometry('1300x800+300+100')  # 窗口大小
    root.resizable(0, 0)  # 禁止缩放
    root.config(bg='#F0F0F0')  # 窗口背景颜色


init_window()
############################################################## 初始化窗口参数 END #########################################################




################################################################# 初始化画布 #############################################################
def load_canvas():
    global canvas, file_temp
    canvas = tk.Canvas(root, bg='black', width=640, height=480)
    canvas.place(x=150, y=60)

load_canvas()
############################################################### 初始化画布 END ###########################################################




################################################################## 重置画布 ##############################################################
def reset_canvas():
    global canvas, file_temp, bg
    bg_ = cv2.imread(bg)
    bg_ = imgConvert.convert(bg_, True)
    file_temp = bg_
    canvas.create_image(0, 0, anchor='nw', image=bg_, tag='bg')
################################################################ 重置画布 END ###########################################################




############################################################### 初始化日志框 ###########################################################
def load_log_box():
    global log_box, root
    streamHandlerBox = LoggerBox(root, width=125, height=12)
    streamHandlerBox.place(x=80, y=560)
    log_box = logging.getLogger('log')
    log_box.setLevel(logging.INFO)
    log_box.addHandler(logging.StreamHandler(streamHandlerBox))

load_log_box()
############################################################## 初始化日志框 END ##########################################################




############################################################# 输出同时打印日志信息 #########################################################
def print_save_log(text, startTime=True):
    global log_box, log_path
    file_name = log_path + get_time(time_format="day") + ".log"
    # 开始加时间
    if startTime:
        text = get_time(time_format="sec") + ": " + text
    # 不换行直接拼接文本内容
    log_box.info(text)
    with open(file_name, 'a+') as f:
        f.write(text)
        f.write('\n')
########################################################## 输出同时打印日志信息 END #######################################################





############################################################## 加载设备选择控件 ############################################################
def load_device_widget(state=True):
    device_var = tk.IntVar()
    # 设备标签
    device_label = tk.Label(root, width=13, height=1, text='empty')
    device_label.place(x=860, y=102)
    device_dict = {0: "CPU", 1: "GPU"}
    device_var.set(0)
    device_label.config(text='计算设备「' + device_dict[device_var.get()] + '」')

    # 设备选择触发函数
    def device_trigger():
        global computer_device
        device_label.config(text='计算设备「' + device_dict[device_var.get()] + '」')
        if device_var.get() == 0:
            computer_device = "cpu"
        if device_var.get() == 1:
            computer_device = "gpu"

    # CPU Radiobutton
    cpu_ra = tk.Radiobutton(root, text='CPU', variable=device_var, value=0, command=device_trigger)
    cpu_ra.place(x=1030, y=100)
    # GPU Radiobutton
    gpu_ra = tk.Radiobutton(root, text='GPU', variable=device_var, value=1, command=device_trigger)
    gpu_ra.place(x=1120, y=100)

    if not state:
        cpu_ra.config(state='disable')
        gpu_ra.config(state='disable')

load_device_widget() if torch.cuda.is_available() else load_device_widget(state=False)
############################################################ 加载设备选择控件 END ###########################################################




############################################################# 加载检测类型控件 ############################################################
def load_type_widget():
    type_var = tk.IntVar()
    # 设备标签
    type_Label = tk.Label(root, width=15, height=1, text='empty')
    type_Label.place(x=860, y=162)

    type_dict = {0: "文件", 1: "摄像头"}

    type_var.set(1)
    type_Label.config(text='检测类型「' + type_dict[type_var.get()] + '」')

    # 设备选择触发函数
    def type_trigger():
        global detect_type, select_file_entry, select_file_Btn
        type_Label.config(text='检测类型「' + type_dict[type_var.get()] + '」')
        if type_var.get() == 0:
            detect_type = "file"
            select_file_entry.config(state='normal')
            select_file_Btn.config(state='normal')
        if type_var.get() == 1:
            detect_type = "camera"
            select_file_entry.config(state='disable')
            select_file_Btn.config(state='disable')


    image_ra = tk.Radiobutton(root, text='文件', variable=type_var, value=0, command=type_trigger)
    image_ra.place(x=1030, y=160)
    # 视频 Radiobutton
    video_ra = tk.Radiobutton(root, text='摄像头', variable=type_var, value=1, command=type_trigger)
    video_ra.place(x=1120, y=160)


load_type_widget()
############################################################ 加载检测类型控件 END ##########################################################




############################################################### conf阈值控件 ##############################################################
def conf_thres_widget():
    conf_thres_label = tk.Label(root, text='conf-thres「' + str(conf_thres) + '」', font=("黑体", 9))
    conf_thres_label.place(x=840, y=230)

    def print_conf_thres_selection(value):
        global conf_thres
        conf_thres = float(value)
        conf_thres_label.config(text='conf-thres「' + str(conf_thres) + '」')

    # iou-thres Scale
    conf_thres_scale = tk.Scale(root, from_=0, to=1, orient=tk.HORIZONTAL, length=250, showvalue=0, tickinterval=0.2,
                                resolution=0.01,
                                command=print_conf_thres_selection)
    conf_thres_scale.place(x=1000, y=230)
    conf_thres_scale.set(conf_thres)


conf_thres_widget()
############################################################# conf阈值控件 END ############################################################




############################################################### iou阈值控件 ##############################################################
def iou_thres_widget():
    iou_thres_label = tk.Label(root, text='iou-thres「' + str(iou_thres) + '」', font=("黑体", 9))
    iou_thres_label.place(x=840, y=290)

    def print_iou_thres_selection(value):
        global iou_thres
        iou_thres = float(value)
        iou_thres_label.config(text='iou-thres「' + str(iou_thres) + '」')

    # iou-thres Scale
    iou_thres_scale = tk.Scale(root, from_=0, to=1, orient=tk.HORIZONTAL, length=250, showvalue=0, tickinterval=0.2,
                               resolution=0.01,
                               command=print_iou_thres_selection)
    iou_thres_scale.place(x=1000, y=290)
    iou_thres_scale.set(iou_thres)


iou_thres_widget()
############################################################ iou阈值控件 END ###########################################################




########################################################## 加载选择图片/视频控件 #########################################################
file_path = tk.StringVar()

def load_select_file_widget(state=True):
    global select_file_entry, select_file_Btn

    def select_file():
        global file_path, file, canvas, root, file_temp

        path = tkf.askopenfilename(
            filetypes=[("Image File", image_formats), ("Video File", video_formats),
                       ("All File", image_formats + video_formats)])

        if path:
            file_path.set(path)
            file = path
            if path.split(".")[-1] in image_formats:
                # 读取图片并显示到窗口
                tkimg = cv2.imread(path)
                tkimg = imgConvert.convert(tkimg, resize=True)
                file_temp = tkimg
                canvas.create_image(0, 0, anchor='nw', image=tkimg, tag='im0')
                root.update()
            # 更新文件
            content.set("detect", "file", path)
            content.write(open("config.ini", "w+", encoding="UTF-8"))

    select_file_entry = tk.Entry(root,
                                 textvariable=file_path,
                                 width=35)
    select_file_entry.place(x=850, y=360)

    file_path.set(file)

    select_file_Btn = tk.Button(root, text="选择文件", command=select_file, relief=tk.RIDGE, bd=2, font=("黑体", 9))
    select_file_Btn.place(x=1175, y=358)

    if not state:
        select_file_entry.config(state='disable')
        select_file_Btn.config(state='disable')


load_select_file_widget(state=False)
######################################################### 加载选择图片/视频控件 END ########################################################




############################################################ 加载选择权重控件 ###########################################################
weight_path = tk.StringVar()
def load_select_weight_widget(state=True):
    def select_weight():
        global weight_path, weight, content, model
        weight_format = ['pt', 'pth']  # 可加载权重格式
        path = tkf.askopenfilename(
            filetypes=[("weight file", weight_format)])
        if path == weight:
            return
        # 模型加载完成后，权重加载不生效
        if path:
            weight_path.set(path)
            weight = path
            # 固定权重保存到配置文件中
            content.set("detect", "weight", path)
            content.write(open("config.ini", "w+", encoding="UTF-8"))
            # 根据模型是否加载输出权重更新成功与否
            if model != None:
                # tkm.showinfo(title="提示", message="模型加载后权重更新不成功，下次生效")
                print_save_log("模型加载后权重更新不成功，下次生效")
            else:
                print_save_log("权重更新成功")
                # tkm.showinfo(title="提示", message="权重更新成功")
        # 用户取消更改权重
        else:
            print_save_log("用户取消更改权重")

    select_weight_entry = tk.Entry(root, textvariable=weight_path, width=35)
    select_weight_entry.place(x=850, y=420)

    weight_path.set(weight)

    select_weight_btn = tk.Button(root, text="选择权重", command=select_weight, relief=tk.RIDGE, bd=2, font=("黑体", 9))
    select_weight_btn.place(x=1175, y=418)

    if not state:
        select_weight_entry.config(state='disable')
        select_weight_btn.config(state='disable')


load_select_weight_widget()

############################################################ 加载选择权重控件 END ###########################################################




############################################################ 加载输出文件夹控件 ###########################################################
output_dir_var = tk.StringVar()

# 初始化输出结果文件夹控件
def load_output_dir_widget(state=True):
    global save_dir

    def select_dir():
        global save_dir

        dir = tkf.askdirectory()
        if dir:
            dir = increment_path(Path(dir) / "exp", exist_ok=False)
            output_dir_var.set(str(dir))
            save_dir = dir

    select_output_dir_entry = tk.Entry(root, textvariable=output_dir_var, width=35)
    select_output_dir_entry.place(x=850, y=480)

    if save_dir == None:
        save_dir = increment_path(Path("runs/detect") / "exp", exist_ok=False)
        output_dir_var.set(save_dir)

    select_output_dir_btn = tk.Button(root, text="输出位置", command=select_dir, relief=tk.RIDGE, bd=2, font=("黑体", 9))
    select_output_dir_btn.place(x=1175, y=478)

    if not state:
        select_output_dir_entry.config(state='disable')
        select_output_dir_btn.config(state='disable')


load_output_dir_widget(state=False)
############################################################ 加载输出文件夹控件 END ###########################################################




############################################################# 自动选择文件夹控件 ############################################################
auto_select_dir_var = tk.IntVar()


def auto_select_dir(state=True):
    def change_auto_select_dir():
        global auto_select_dir_var
        # 选中自动选择文件夹后，锁定无法选择更改文件夹
        if auto_select_dir_var.get() == 1:
            load_output_dir_widget(state=False)
        else:
            load_output_dir_widget(state=True)

    # 自动选择输出文件夹
    auto_select_dir_cbtn = tk.Checkbutton(root, text='自动选择', variable=auto_select_dir_var, onvalue=1, offvalue=0,
                                          command=change_auto_select_dir)
    auto_select_dir_cbtn.place(x=845, y=510)
    auto_select_dir_var.set(1)

    if not state:
        auto_select_dir_cbtn.config(state='disable')


auto_select_dir()
########################################################### 自动选择文件夹控件 END ##########################################################




############################################################### 预加载模型控件 ##############################################################
def load_model_widget():
    def load_model():
        global model, names, computer_device, weight
        device = computer_device
        if computer_device == "gpu":
            device = "0"
        # 加载模型
        model = attempt_load(weight, map_location=device)  # load FP32 model
        # 从模型中获取到类别数组
        names = model.names
        # 初始化日志
        set_logging()

        # tkm.showinfo(title="提示", message="模型加载成功")
        print_save_log(f"权重文件：{weight}")
        print_save_log(f"网络结构\n{model}")
        print_save_log("模型加载成功")

    load_model_btn = tk.Button(root, text='加载模型', command=lambda: MyThread(load_model), relief=tk.RIDGE, bd=2)
    load_model_btn.place(x=40, y=100)


load_model_widget()
############################################################# 预加载模型控件 END #############################################################




############################################################## 加载开始检测控件 #############################################################
def load_camera_widget():
    # 打开摄像头按钮
    open_camera_btn = tk.Button(root, text='开始检测', command=lambda: MyThread(start_detect), relief=tk.RIDGE, bd=2)
    open_camera_btn.place(x=40, y=180)


load_camera_widget()
############################################################ 加载开始检测控件 END ###########################################################




############################################################# 加载停止检测控件 ############################################################
def load_stop_detect_widget():
    def stop_detect():
        global stop_detect
        stop_detect = True

    # 打开摄像头按钮
    stop_detect_btn = tk.Button(root, text='停止检测', command=lambda: MyThread(stop_detect), relief=tk.RIDGE, bd=2)
    stop_detect_btn.place(x=40, y=260)


load_stop_detect_widget()
############################################################ 加载停止检测控件 END ##########################################################




############################################################# 加载重置系统控件 ############################################################
def load_reset_system_widget(state=True):
    global reset_system_btn

    def reset_system():
        global dataset, canvas, model, names
        if dataset != None:
            dataset.cap.release()
            dataset = None
        if model != None:
            model = None
        if names != None:
            names = None

        reset_canvas()
        # tkm.showinfo(title="提示", message="系统重置成功")
        print_save_log("系统重置成功")

    reset_system_btn = tk.Button(root, text='重置系统', command=lambda: MyThread(reset_system), relief=tk.RIDGE, bd=2)
    reset_system_btn.place(x=40, y=340)

    if not state:
        select_weight_entry.config(state='disable')
        select_weight_btn.config(state='disable')


load_reset_system_widget()
############################################################ 加载重置系统控件 END ##########################################################




############################################################## 加载检测结果控件 ############################################################
def load_detect_result_widget():
    def open_result():
        global save_dir

        if save_dir == None:
            print_save_log("暂无运行结果，请检测后再打开")
        else:
            output_dir = str(save_dir)
            # 找不到:说明路径为相对路径 runs\detect\exp4
            if output_dir.find(":") == -1:
                output_dir = os.getcwd() + "\\" + output_dir
            try:
                os.startfile(output_dir)
            except FileNotFoundError:
                print_save_log("暂无结果，请检测后再打开")

    detect_result_btn = tk.Button(root, text='检测结果', command=lambda: MyThread(open_result), relief=tk.RIDGE, bd=2)
    detect_result_btn.place(x=40, y=420)


load_detect_result_widget()
############################################################ 加载检测结果控件 END ##########################################################

# 测试按钮
def test_widget():
    def test():
        pass

    test_btn = tk.Button(root, text='测试功能', command=lambda: MyThread(test), relief=tk.RIDGE, bd=2)
    test_btn.place(x=50, y=500)


test_widget()

################################################################## 获取时间 ##############################################################
def get_time(time_format="day"):
    now_time = str(datetime.datetime.now())
    sec_time = now_time.split(".")[0]
    day_time = now_time.split(" ")[0]
    if time_format == "day":
        return day_time
    elif time_format == "sec":
        return sec_time
################################################################ 获取时间 END ############################################################




################################################################# 手动关闭程序 ############################################################
def close():
    if tkm.askokcancel("关闭", "是否关闭程序"):
        print_save_log("程序退出")
        cv2.destroyAllWindows()
        root.destroy()
############################################################### 手动关闭程序 END ##########################################################


root.protocol("WM_DELETE_WINDOW", close)
root.mainloop()
